#include "SPmisc.h"
#include <assert.h>

///-------------------------------------------------------------------------------
///-------------------------------------------------------------------------------
namespace SP
{
	//--------------------------------------------------------------------------------------
	SPTimer::SPTimer()
	{
		m_bTimerStopped = true;
		m_llQPFTicksPerSec = 0;

		m_llStopTime = 0;
		m_llLastElapsedTime = 0;
		m_llBaseTime = 0;

		// Use QueryPerformanceFrequency to get the frequency of the counter
		LARGE_INTEGER qwTicksPerSec = { 0 };
		QueryPerformanceFrequency( &qwTicksPerSec );
		m_llQPFTicksPerSec = qwTicksPerSec.QuadPart;
	}


	//--------------------------------------------------------------------------------------
	void SPTimer::Reset()
	{
		LARGE_INTEGER qwTime = GetAdjustedCurrentTime();

		m_llBaseTime = qwTime.QuadPart;
		m_llLastElapsedTime = qwTime.QuadPart;
		m_llStopTime = 0;
		m_bTimerStopped = FALSE;
	}


	//--------------------------------------------------------------------------------------
	void SPTimer::Start()
	{
		// Get the current time
		LARGE_INTEGER qwTime = { 0 };
		QueryPerformanceCounter( &qwTime );

		if( m_bTimerStopped )
			m_llBaseTime += qwTime.QuadPart - m_llStopTime;
		m_llStopTime = 0;
		m_llLastElapsedTime = qwTime.QuadPart;
		m_bTimerStopped = FALSE;
	}


	//--------------------------------------------------------------------------------------
	void SPTimer::Stop()
	{
		if( !m_bTimerStopped )
		{
			LARGE_INTEGER qwTime = { 0 };
			QueryPerformanceCounter( &qwTime );
			m_llStopTime = qwTime.QuadPart;
			m_llLastElapsedTime = qwTime.QuadPart;
			m_bTimerStopped = TRUE;
		}
	}


	//--------------------------------------------------------------------------------------
	void SPTimer::Advance()
	{
		m_llStopTime += m_llQPFTicksPerSec / 10;
	}


	//--------------------------------------------------------------------------------------
	double SPTimer::GetAbsoluteTime()
	{
		LARGE_INTEGER qwTime = { 0 };
		QueryPerformanceCounter( &qwTime );

		double fTime = qwTime.QuadPart / ( double )m_llQPFTicksPerSec;

		return fTime;
	}

	//--------------------------------------------------------------------------------------
	double SPTimer::GetTime()
	{
		LARGE_INTEGER qwTime = GetAdjustedCurrentTime();

		double fAppTime = ( double )( qwTime.QuadPart - m_llBaseTime ) / ( double )m_llQPFTicksPerSec;

		return fAppTime;
	}


	//--------------------------------------------------------------------------------------
	void SPTimer::GetTimeValues( double* pfTime, double* pfAbsoluteTime, float* pfElapsedTime )
	{
		assert( pfTime && pfAbsoluteTime && pfElapsedTime );

		LARGE_INTEGER qwTime = GetAdjustedCurrentTime();

		float fElapsedTime = ( float )( ( double )( qwTime.QuadPart - m_llLastElapsedTime ) / ( double )
			m_llQPFTicksPerSec );
		m_llLastElapsedTime = qwTime.QuadPart;

		// Clamp the timer to non-negative values to ensure the timer is accurate.
		// fElapsedTime can be outside this range if processor goes into a 
		// power save mode or we somehow get shuffled to another processor.  
		// However, the main thread should call SetThreadAffinityMask to ensure that 
		// we don't get shuffled to another processor.  Other worker threads should NOT call 
		// SetThreadAffinityMask, but use a shared copy of the timer data gathered from 
		// the main thread.
		if( fElapsedTime < 0.0f )
			fElapsedTime = 0.0f;

		*pfAbsoluteTime = qwTime.QuadPart / ( double )m_llQPFTicksPerSec;
		*pfTime = ( qwTime.QuadPart - m_llBaseTime ) / ( double )m_llQPFTicksPerSec;
		*pfElapsedTime = fElapsedTime;
	}


	//--------------------------------------------------------------------------------------
	float SPTimer::GetElapsedTime()
	{
		LARGE_INTEGER qwTime = GetAdjustedCurrentTime();

		double fElapsedTime = ( float )( ( double )( qwTime.QuadPart - m_llLastElapsedTime ) / ( double )
			m_llQPFTicksPerSec );
		m_llLastElapsedTime = qwTime.QuadPart;

		// See the explanation about clamping in SPTimer::GetTimeValues()
		if( fElapsedTime < 0.0f )
			fElapsedTime = 0.0f;

		return ( float )fElapsedTime;
	}


	//--------------------------------------------------------------------------------------
	// If stopped, returns time when stopped otherwise returns current time
	//--------------------------------------------------------------------------------------
	LARGE_INTEGER SPTimer::GetAdjustedCurrentTime()
	{
		LARGE_INTEGER qwTime;
		if( m_llStopTime != 0 )
			qwTime.QuadPart = m_llStopTime;
		else
			QueryPerformanceCounter( &qwTime );
		return qwTime;
	}

	//--------------------------------------------------------------------------------------
	bool SPTimer::IsStopped()
	{
		return m_bTimerStopped;
	}

	//--------------------------------------------------------------------------------------
	// Limit the current thread to one processor (the current one). This ensures that timing code 
	// runs on only one processor, and will not suffer any ill effects from power management.
	// See "Game Timing and Multicore Processors" for more details
	//--------------------------------------------------------------------------------------
	void SPTimer::LimitThreadAffinityToCurrentProc()
	{
		HANDLE hCurrentProcess = GetCurrentProcess();

		// Get the processor affinity mask for this process
		DWORD_PTR dwProcessAffinityMask = 0;
		DWORD_PTR dwSystemAffinityMask = 0;

		if( GetProcessAffinityMask( hCurrentProcess, &dwProcessAffinityMask, &dwSystemAffinityMask ) != 0 &&
			dwProcessAffinityMask )
		{
			// Find the lowest processor that our process is allows to run against
			DWORD_PTR dwAffinityMask = ( dwProcessAffinityMask & ( ( ~dwProcessAffinityMask ) + 1 ) );

			// Set this as the processor that our thread must always run against
			// This must be a subset of the process affinity mask
			HANDLE hCurrentThread = GetCurrentThread();
			if( INVALID_HANDLE_VALUE != hCurrentThread )
			{
				SetThreadAffinityMask( hCurrentThread, dwAffinityMask );
				CloseHandle( hCurrentThread );
			}
		}

		CloseHandle( hCurrentProcess );
	}
}

///-------------------------------------------------------------------------------
///-------------------------------------------------------------------------------
namespace SP
{
	// return float between -1.0f and 1.0f
	float SPRPresent()
	{
		float ret = ( float )( ( rand() % 20000 ) - 10000 );
		return ret / 10000.0f;
	}


	//----------------------------------------------------------
	SPDistributionFloatConstant::SPDistributionFloatConstant( FLOAT init ) 
		: m_fConstant( init )
	{

	}

	FLOAT SPDistributionFloatConstant::CalcuValue()
	{
		return m_fConstant;
	}

	void SPDistributionFloatConstant::GetFileBufferInfo( UINT* type, FLOAT* constant, FLOAT* max, FLOAT* min)
	{
		*type = 0;
		*constant = m_fConstant;
	}		

	SPDistributionFloat* SPDistributionFloatConstant::SpawnInstance()
	{
		return  new SPDistributionFloatConstant( m_fConstant );
	}

	//----------------------------------------------------------
	SPDistributionFloatUniform::SPDistributionFloatUniform( FLOAT max, FLOAT min )
		: m_fMax(max), m_fMin(min)
	{

	}

	FLOAT SPDistributionFloatUniform::CalcuValue( )
	{
		float scale = ( (float) ( rand() % 10000 ) / 10000 );
		return m_fMin * scale + m_fMax * ( 1 - scale );
	}

	void SPDistributionFloatUniform::GetFileBufferInfo( UINT* type, FLOAT* constant, FLOAT* max, FLOAT* min)
	{
		*type = 1;
		*max = m_fMax;
		*min = m_fMin;
	}

	SPDistributionFloat* SPDistributionFloatUniform::SpawnInstance()
	{
		return new SPDistributionFloatUniform( m_fMax, m_fMin );
	}
	/*
	//------------------------------------
	void SPDistributionVectorConstant::CalcuValue( XMFLOAT3& v )
	{
		v = m_vConstant;
	}


	//------------------------------------
	void SPDistributionVectorUniform::CalcuValue( XMFLOAT3& v )
	{
		float scale = ( ( float ) ( rand() % 10000 ) / 10000 );
		v.x = m_vMin.x * scale + m_vMax.x * ( 1 - scale );
		v.y = m_vMin.y * scale + m_vMax.y * ( 1 - scale );
		v.z = m_vMin.z * scale + m_vMax.z * ( 1 - scale );
	}
	*/

	//----------------------------------------------------------
	// RawDistributionFloat
	//----------------------------------------------------------
	SPRawDistributionFloat::SPRawDistributionFloat() 
		: m_pDFx( new SPDistributionFloatConstant(0.0f) )
	{

	}

	SPRawDistributionFloat::SPRawDistributionFloat( SPRawDistributionFloat* archetype )
	{
		m_pDFx = archetype->m_pDFx->SpawnInstance();
	}

	SPRawDistributionFloat::~SPRawDistributionFloat()
	{
		//SAFE_DELETE( m_pDFx );
	}


	void SPRawDistributionFloat::SafeGet( SPDistributionFloat** px )
	{
		*px = m_pDFx;
	}

	void SPRawDistributionFloat::SafeSet( SPDistributionFloat* px )
	{
		if ( px )
		{
			SAFE_DELETE( m_pDFx );
			m_pDFx = px->SpawnInstance();
		}
	}

	void SPRawDistributionFloat::GetFileBufferInfo( UINT* type, FLOAT* constant, FLOAT* max, FLOAT* min )
	{
		m_pDFx->GetFileBufferInfo( type, constant, max, min );
	}

	void SPRawDistributionFloat::InitByFileBufferInfo( UINT type, FLOAT constant, FLOAT max, FLOAT min )
	{
		if ( type == 0 )
		{
			m_pDFx = new SPDistributionFloatConstant( constant );
		}
		else
		{
			m_pDFx = new SPDistributionFloatUniform( max, min );
		}
	}

	//----------------------------------------------------------
	// RawDistributionVector
	//----------------------------------------------------------
	SPRawDistributionVector::SPRawDistributionVector()
		: m_pDFx( new SPDistributionFloatConstant(0.0f) ),
		m_pDFy( new SPDistributionFloatConstant(0.0f) ),
		m_pDFz( new SPDistributionFloatConstant(0.0f) )
	{

	}

	SPRawDistributionVector::SPRawDistributionVector( SPRawDistributionVector* archetype )
	{
		m_pDFx = archetype->m_pDFx->SpawnInstance();
		m_pDFy = archetype->m_pDFy->SpawnInstance();
		m_pDFz = archetype->m_pDFz->SpawnInstance();
	}

	SPRawDistributionVector::~SPRawDistributionVector()
	{
		SAFE_DELETE( m_pDFx );
		SAFE_DELETE( m_pDFy );
		SAFE_DELETE( m_pDFz );
	}

	void SPRawDistributionVector::SafeGet( SPDistributionFloat** ppx, SPDistributionFloat** ppy, SPDistributionFloat** ppz )
	{
		*ppx = m_pDFx;
		*ppy = m_pDFy;
		*ppz = m_pDFz;
	}

	void SPRawDistributionVector::SafeSet( SPDistributionFloat* px, SPDistributionFloat* py, SPDistributionFloat* pz )
	{
		if ( px )
		{
			SAFE_DELETE( m_pDFx );
			m_pDFx = px->SpawnInstance();
		}
		if ( py )
		{
			SAFE_DELETE( m_pDFy );
			m_pDFy = py->SpawnInstance();
		}
		if( pz )
		{
			SAFE_DELETE( m_pDFz );			
			m_pDFz = pz->SpawnInstance();
		}
	}

	void SPRawDistributionVector::GetFileBufferInfo( UINT* typex, FLOAT* constantx, FLOAT* maxx, FLOAT* minx, 
		UINT* typey, FLOAT* constanty, FLOAT* maxy, FLOAT* miny, 
		UINT* typez, FLOAT* constantz, FLOAT* maxz, FLOAT* minz )
	{
		m_pDFx->GetFileBufferInfo( typex, constantx, maxx, minx );
		m_pDFy->GetFileBufferInfo( typey, constanty, maxy, miny );
		m_pDFz->GetFileBufferInfo( typez, constantz, maxz, minz );
	}

	void SPRawDistributionVector::InitByFileBufferInfo(	UINT typex, FLOAT constantx, FLOAT maxx, FLOAT minx, 
		UINT typey, FLOAT constanty, FLOAT maxy, FLOAT miny, 
		UINT typez, FLOAT constantz, FLOAT maxz, FLOAT minz )
	{
		// x
		if ( typex == 0 )
		{
			m_pDFx = new SPDistributionFloatConstant( constantx );
		}
		else
		{
			m_pDFx = new SPDistributionFloatUniform( maxx, minx );
		}

		// y
		if ( typey == 0 )
		{
			m_pDFy = new SPDistributionFloatConstant( constanty );
		}
		else
		{
			m_pDFy = new SPDistributionFloatUniform( maxy, miny );
		}

		// z
		if ( typez == 0 )
		{
			m_pDFz = new SPDistributionFloatConstant( constantz );
		}
		else
		{
			m_pDFz = new SPDistributionFloatUniform( maxz, minz );
		}
	}


}